/**
 * Copyright (C), 2001-2031, www.bosssoft.com.cn
 * FileName: JwtTokenBuilder.java
 * Author: Camera
 * Date: 2024/8/4 18:00
 * Description:
 * <p>
 * History:
 * Date          Author   Version  Desc
 * 2024-01-01    bosssoft  1.0.0   initialize this file
 */
package com.bosssoft.common.token;

import cn.hutool.core.net.URLEncoder;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * @className: JwtTokenBuilder
 * @description: 封装了JWT规范的token的基本方法，采用Builder模式构建 JwtToken
 * @author: Camera
 * @date: 2024/8/418:00
 * @since 1.0
 **/
@Component
public class JwtTokenBuilder extends AbstractTokenBuilder {
    /**
     * 签名算法常量
     */
    public static final String ALG_SHA2 = "sha2";
    /**
     * 签名算法常量
     */
    public static final String ALG_MD5 = "md5";

    /**
     * token头 为JSONString 格式的字符串
     */
    private String head;
    /**
     * 载荷 为JSONString 格式的字符串
     */
    private String payload;
    /**
     * 签名 为String
     */
    private String signature;


    /**
     * 构建JWT令牌的头部信息
     *
     * @param head 包含JWT头部信息的JSONObject对象。
     * @return 返回当前的AbstractTokenBuilder实例，以便进行链式调用。
     * @throws RuntimeException 如果传入的头部信息为空或缺少必要的"alg"字段，则抛出运行时异常。
     */
    @Override
    public AbstractTokenBuilder buildHead(JSONObject head) {
        if (head == null || head.get("alg") == null) {
            throw new RuntimeException("构建token头参数不能为空");
        }
        if (head.get("typ") == null) {
            head.put("typ", "JWT");
        }
        //是否需要加密存入　head
        this.head = head.toJSONString();
        return this;
    }

    /**
     * 构建JWT令牌的有效负载部分
     *
     * @param payload 包含JWT有效负载信息的JSONObject对象
     * @return 返回当前的AbstractTokenBuilder实例，以便进行链式调用。
     * @throws RuntimeException 如果传入的有效负载信息为空，则抛出运行时异常。
     */
    @Override
    public AbstractTokenBuilder buildPayload(JSONObject payload) {
        if (payload == null) {
            throw new RuntimeException("构建token payload不能为空");
        }
        // 加密存入 body
        this.payload = payload.toJSONString();
        return this;
    }

    /**
     * 构建JWT令牌的签名部分。
     *
     * @param secret 用于生成签名的密钥。
     * @return 返回当前的AbstractTokenBuilder实例，以便进行链式调用。
     * @throws RuntimeException 如果生成签名过程中出现错误，则抛出运行时异常。
     */
    @Override
    public AbstractTokenBuilder buildSignature(String secret) {
        // 按签名协议构建生成签名存入
        this.signature = generateSignature(this.head, this.payload, secret);
        return this;
    }

    /**
     * 完成JWT令牌的构建过程。
     *
     * @return 返回当前的AbstractTokenBuilder实例，表示构建过程已完成。
     */
    @Override
    public AbstractTokenBuilder build() {
        return this;
    }

    /**
     * 验证JWT令牌的签名是否有效。
     *
     * @param head      JWT令牌的头部信息。
     * @param payload   JWT令牌的有效负载信息。
     * @param signature JWT令牌的签名。
     * @param secret    用于生成和验证签名的密钥。
     * @return 如果签名验证通过，则返回true；否则返回false。
     * @throws RuntimeException 如果传入的参数缺失，则抛出运行时异常。
     */
    @Override
    public boolean verifySignature(JSONObject head, JSONObject payload, String signature, String secret) {
        if (head == null || payload == null || StringUtil.isEmpty(signature) || StringUtil.isEmpty(signature)) {
            throw new RuntimeException("签名验证方法参数缺失");
        }
        // 按签名协议构建生成签名存入
        return signature.equals(generateSignature(head.toJSONString(), payload.toJSONString(), secret));
    }

    /**
     * 验证当前JWT令牌的签名是否有效。
     *
     * @param signature 当前JWT令牌的签名。
     * @param secret    用于生成和验证签名的密钥。
     * @return 如果签名验证通过，则返回true；否则返回false。
     */
    @Override
    public boolean verifySignature(String signature, String secret) {
        return signature.equals(generateSignature(this.head, this.payload, secret));
    }

    /**
     * 将当前JWT令牌的所有部分（头部、有效负载和签名）转换为一个JSONObject对象。
     *
     * @return 包含JWT令牌所有部分的JSONObject对象。
     */
    @Override
    public JSONObject toJsonObject() {
        JSONObject jwtTokenJSON = new JSONObject();
        jwtTokenJSON.put("head", this.head);
        jwtTokenJSON.put("payload", this.payload);
        jwtTokenJSON.put("signature", this.signature);
        return jwtTokenJSON;
    }

    /**
     * 将当前JWT令牌的所有部分（头部、有效负载和签名）转换为一个字符串列表。
     *
     * @return 包含JWT令牌所有部分的List<String>对象。
     */
    @Override
    public List<String> toJsonList() {
        List<String> jwtTokenLst = new ArrayList<>();
        jwtTokenLst.add(head);
        jwtTokenLst.add(payload);
        jwtTokenLst.add(signature);
        return jwtTokenLst;
    }

    /**
     * 将当前JWT令牌的所有部分（头部、有效负载和签名）转换为一个字符串表示形式。
     *
     * @return 包含JWT令牌所有部分的格式化字符串，格式为 "head.payload.signature"。
     */
    @Override
    public String toString() {
        // head payload 为 JSONString 类型
        return head + "." + payload + "." + signature;
    }

    /**
     * 生成JWT令牌的签名。
     *
     * @param head    JWT令牌的头部信息，JSON字符串格式。
     * @param payload JWT令牌的有效负载信息，JSON字符串格式。
     * @param secret  用于生成签名的密钥。
     * @return 生成的签名字符串。
     */
    @Override
    protected String generateSignature(String head, String payload, String secret) {
        String signature = "";
        byte[] signContent = (URLEncoder.createDefault().encode(head, UTF_8).toString()
                + "." + URLEncoder.createDefault().encode(payload, UTF_8).toString()).getBytes(UTF_8);
        switch (JSONObject.parseObject(head).get("alg").toString().toUpperCase()) {
            case "SHA2":
                signature = EncryptSimpleUtil.sha2(signContent, secret);
                break;
            case "MD5":
                signature = EncryptSimpleUtil.md5(signContent, secret);
                break;
        }
        return signature;
    }
}

